package com.example.apidocserver.application.event.impl;

import com.example.apidocserver.application.event.ApiEvent;
import com.example.apidocserver.application.event.DocLoadEvent;
import com.example.apidocserver.application.event.SystemEvent;
import com.example.apidocserver.domain.document.dto.UploadMenuApiDto;
import com.example.apidocserver.domain.document.entity.ApiDocument;
import com.example.apidocserver.domain.document.entity.DocMenu;
import com.example.apidocserver.domain.document.event.*;
import com.example.apidocserver.domain.space.event.SpaceDomainService;
import com.example.apidocserver.domain.system.entity.AllEnum;
import com.example.apidocserver.domain.user.entity.DocUsrHis;
import com.example.apidocserver.domain.user.event.UserDocInfoEventService;
import com.example.apidocserver.infrastructure.config.input.ApiYmlConfig;
import com.example.apidocserver.infrastructure.constants.SystemConstants;
import com.example.apidocserver.infrastructure.util.MyDateUtils;
import com.example.apidocserver.infrastructure.util.document.EntityToMarkdown;
import com.example.apidocserver.infrastructure.util.document.EntityToWord;
import com.example.apidocserver.infrastructure.util.document.EntityToYuque;
import com.example.apidocserver.infrastructure.util.user.UserUtil;
import com.example.apidocserver.interfaces.vo.ApiInfoAllVo;
import com.example.apidocserver.interfaces.vo.ApiParamDescVo;
import com.example.apidocserver.interfaces.vo.DocumentVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

import static com.example.apidocserver.infrastructure.constants.DocToYuqueConstants.H_LIMIT;

/**
 * 文档上传下载
 *
 * @author xyqlg
 */
@Service
@Slf4j
public class DocLoadEventImpl implements DocLoadEvent {
    @Resource
    private SpaceDomainService spaceDomainService;

    @Resource
    private DocumentDomainEvent documentDomainEvent;

    @Resource
    private ApiInfoDomainEvent apiInfoDomainEvent;

    @Resource
    private ParamHisDomainEvent paramHisDomainEvent;

    @Resource
    private FieldParamDomainEvent fieldParamDomainEvent;

    @Resource
    private DocLoadDomainEvent docLoadDomainEvent;

    @Resource
    private UserDocInfoEventService userDocInfoEventService;

    @Resource
    private ApiEvent apiEvent;

    @Resource
    private SystemEvent systemEvent;

    @Resource
    private ApiYmlConfig apiYmlConfig;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inputMenuApi(MultipartFile file, Integer spaceId, Integer docId) throws IOException {
        // 1.检查工作空间、文档存在校验
        spaceDomainService.checkById(spaceId);
        documentDomainEvent.checkById(docId);

        // 2.将json解析为插入实体对象
        UploadMenuApiDto menuApiDto = docLoadDomainEvent.jonsToEntities(file, spaceId, docId);
        if (CollectionUtils.isEmpty(menuApiDto.getMenuNodeList())) {
            return;
        }

        // 3.插入接口信息
        apiInfoDomainEvent.insertOrFindBatch(menuApiDto.getApiInfoList());
        // 4.插入目录信息
        documentDomainEvent.insertMenuTrees(menuApiDto);
        // 5.插入参数示例
        List<ApiParamDescVo> apiParamDescList = paramHisDomainEvent.insertBatchForInput(menuApiDto);
        // 6.插入参数说明
        fieldParamDomainEvent.insertBatchForInput(apiParamDescList, menuApiDto.getUserId(), menuApiDto.getNow());
    }

    @Override
    public String downApiInfoOneById(XWPFDocument document, Integer apiId, Integer envId) {
        // 查询接口信息
        ApiInfoAllVo apiInfoAllVo = apiEvent.getApiInfo(apiId, envId, false);

        // 设置字段类型
        initAllEnum();

        // 设置文档信息
        EntityToWord.setApiInfo(document, apiInfoAllVo.getApiInfo());
        EntityToWord.setParamDesc(document, apiInfoAllVo.getApiParamDescVo());
        EntityToWord.setParamHis(document, apiInfoAllVo.getCurrentParamHis());
        return apiInfoAllVo.getApiInfo().getApiName() + MyDateUtils.getNowString(MyDateUtils.DT_FILE_NAME) + ".docx";
    }

    @Override
    public String generateMdApiById(Integer apiId, Integer envId, Integer startTitleNo) {
        ApiInfoAllVo apiInfo = apiEvent.getApiInfo(apiId, envId, false);
        initAllEnum();
        return EntityToMarkdown.getMarkdownStr(apiInfo, EntityToWord.startTitleNo(startTitleNo));
    }

    @Override
    public String generateMdDocById(Integer docId, Integer envId, Integer startTitleNo) {
        List<DocMenu> currentMenu = getDocMenu(docId);
        StringBuilder sb = new StringBuilder();

        int startNo = EntityToWord.startTitleNo(startTitleNo);
        buildMdStr(sb, currentMenu, envId, startNo, 0);
        return sb.toString();
    }

    @Override
    public String generateYuqueApiInfoById(Integer apiId, Integer envId, Integer startTitleNo) {
        ApiInfoAllVo apiInfo = apiEvent.getApiInfo(apiId, envId, false);
        initAllEnum();
        return EntityToYuque.toYuqueStr(apiInfo, EntityToWord.startTitleNo(startTitleNo));
    }

    @Override
    public String generateYuqueDocById(Integer docId, Integer envId, Integer startTitleNo) {
        List<DocMenu> currentMenu = getDocMenu(docId);
        StringBuilder sb = new StringBuilder();

        int startNo = EntityToWord.startTitleNo(startTitleNo);
        buildYuqueStr(sb, currentMenu, envId, startNo, 0);
        return sb.toString();
    }

    private List<DocMenu> getDocMenu(Integer docId) {
        DocumentVo documentVo = new DocumentVo();
        ApiDocument apiDocument = new ApiDocument().setId(docId);
        documentVo.setCreateFlag(UserUtil.createDocumentFlag()).setUpdateFlag(true).setCurrentDocument(apiDocument);
        DocUsrHis usrLastInfo = userDocInfoEventService.getUsrLastInfo();
        documentDomainEvent.setCurrentMenu(documentVo, usrLastInfo);
        return documentVo.getCurrentMenus();
    }

    private void buildMdStr(StringBuilder sb, List<DocMenu> currentMenus, Integer envId, int startTitleNo, int lvl) {
        if (CollectionUtils.isEmpty(currentMenus) || lvl > H_LIMIT) {
            return;
        }
        log.info("lvl: {}", lvl);
        for (DocMenu menu : currentMenus) {
            if (SystemConstants.MENU_URI_TYPE.equals(menu.getMenuType())) {
                if (Objects.nonNull(menu.getApiId())) {
                    sb.append(generateMdApiById(menu.getApiId(), envId, startTitleNo));
                }
            } else {
                EntityToMarkdown.setTitle(sb, menu.getMenuName(), startTitleNo);
                buildMdStr(sb, menu.getChildren(), envId, startTitleNo + 1, lvl + 1);
            }
        }
    }

    private void buildYuqueStr(StringBuilder sb, List<DocMenu> currentMenus, Integer envId, int startTitleNo, int lvl) {
        if (CollectionUtils.isEmpty(currentMenus) || lvl > H_LIMIT) {
            return;
        }
        log.info("lvl: {}", lvl);
        for (DocMenu menu : currentMenus) {
            if (SystemConstants.MENU_URI_TYPE.equals(menu.getMenuType())) {
                if (Objects.nonNull(menu.getApiId())) {
                    sb.append(generateYuqueApiInfoById(menu.getApiId(), envId, startTitleNo));
                }
            } else {
                EntityToYuque.setTitle(sb, menu.getMenuName(), startTitleNo);
                buildYuqueStr(sb, menu.getChildren(), envId, startTitleNo + 1, lvl + 1);
            }
        }
    }

    private void initAllEnum() {
        if (EntityToWord.typeIsNull()) {
            AllEnum allEnum = systemEvent.getByType(apiYmlConfig.fieldTypeId);
            EntityToWord.setTypeMap(allEnum);
        }
    }
}
